package com.longcai.cm.api.api4login;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson.JSON;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.longcai.cm.api.request.RegisterEntity;
import com.longcai.cm.api.response.MemberLoginInfo;
import com.longcai.cm.auth.Login;
import com.longcai.cm.auth.LoginUser;
import com.longcai.cm.auth.token.JWTHelper;
import com.longcai.cm.auth.token.LoginUserInfo;
import com.longcai.cm.base.aop.RepeatSubmit;
import com.longcai.cm.bean.R;
import com.longcai.cm.common.constant.ConstantValues;
import com.longcai.cm.common.constant.RedisPrefix;
import com.longcai.cm.common.utils.RequestUtil;
import com.longcai.cm.domain.FaMember;
import com.longcai.cm.domain.JaSmsRecord;
import com.longcai.cm.service.FaMemberService;
import com.longcai.cm.service.JaSmsRecordService;
import com.longcai.cm.service.RedisService;
import com.longcai.cm.utils.sms.ApiRegisterModel;
import com.longcai.cm.utils.sms.SmsClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * @author libingxing
 */
@RestController
@Api(tags = "登录注册相关接口")
@RequestMapping("/api/login")
public class ApiLoginController {

    @Resource
    private FaMemberService memberService;

    @Resource
    private RedisService redisService;

    @Resource
    private JaSmsRecordService smsRecordService;

    @RepeatSubmit
    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "获取短信验证码")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "phoneNum", value = "手机号", dataType = "string", required = true),
            @ApiImplicitParam(name = "sign", value = "加密值", dataType = "string", required = true)
    })
    @PostMapping(value = "/getPhoneCheckCode")
    public R<String> getPhoneCheckCode(
            @RequestParam String phoneNum,
            @RequestParam String sign,
            HttpServletRequest request
    ) {
        String salt = "ZHE_GE_JIE_KOU_YI_JIN_JIA_MI_LE";
        final String md5 = MD5.create().digestHex(phoneNum + salt);
        if (!md5.equalsIgnoreCase(sign)) {
            return R.error("权限不足");
        }
        final String ipAddr = RequestUtil.getIpAddr(request);
        System.err.println(ipAddr);
        JaSmsRecord smsRecord = smsRecordService.lambdaQuery()
                .eq(JaSmsRecord::getMobile, phoneNum)
                .orderByDesc(JaSmsRecord::getCt)
                .last("limit 1")
                .one();
        if (smsRecord != null) {
//            当前时间 - 发送时间 = 间隔时间
            long left = System.currentTimeMillis() - smsRecord.getCt().getTime();
//            1分钟
            if (left < ConstantValues.ONE_MINUTE_MIL) {
                return R.error("发送请求过于频繁");
            }
        }

        String verifyCode = RandomStringUtils.randomNumeric(6);
//        发送短信
        String errorMsg = SmsClient.sendMsg(phoneNum, verifyCode);
        if (StringUtils.isEmpty(errorMsg)) {
//            成功记录入库
            JaSmsRecord record = new JaSmsRecord();
            record.setMobile(phoneNum);
            record.setCode(verifyCode);
            record.setCt(DateUtil.date());
            record.setIpSource(ipAddr);
            smsRecordService.save(record);
//            缓存用于核对
            ApiRegisterModel model = new ApiRegisterModel();
            model.setVerifyCode(verifyCode);
            long expireTime = System.currentTimeMillis() + ConstantValues.VERIFY_CODE_EXPIRE;
            String md5Code = MD5.create().digestHex(RedisPrefix.LOGIN_USER + phoneNum + expireTime + verifyCode);
            model.setExpireTime(expireTime + "");
            model.setMd5Code(md5Code);
            redisService.set(RedisPrefix.VERIFY_CODE + phoneNum, JSON.toJSONString(model), ConstantValues.VERIFY_CODE_EXPIRE);
            return R.success("发送成功" + verifyCode);
        } else {
            return R.error(errorMsg);
        }
    }

    @PostMapping("/register")
    @ApiOperation("注册")
    public R<MemberLoginInfo> register(
            RegisterEntity entity
    ) {
        final String errorMsg = smsRecordService.checkVerifyCode(entity.getMobile(), entity.getCheckCode());
        if (StringUtils.isNotBlank(errorMsg)) {
            return R.error(errorMsg);
        }
        final FaMember old = memberService.lambdaQuery().eq(FaMember::getMobile, entity.getMobile()).one();
        if (old != null) {
            return R.error("您的账号已存在");
        }
        final FaMember param = new FaMember();
        BeanUtils.copyProperties(entity, param);
        if (StringUtils.isNotBlank(entity.getInviteCode())) {
            final FaMember parent = memberService.lambdaQuery().eq(FaMember::getMobile, entity.getInviteCode()).one();
            if (parent != null) {
                if (parent.getPushAuth() == 1) {
                    param.setPid(parent.getId());
                }
            } else {
                return R.error("推广人信息异常");
            }
        }
        FaMember user = memberService.register(param);
        final String token = convertToken(user.getId() + "");
        final MemberLoginInfo result = new MemberLoginInfo();
        BeanUtils.copyProperties(user, result);
        result.setToken(token);
        return R.success(result);
    }

    @PostMapping("/apiLogin")
    @ApiOperation("登录")
    @ApiOperationSupport(order = 2)
    @ApiImplicitParams({
            @ApiImplicitParam(value = "电话号码", name = "mobile", dataType = "string", required = true),
            @ApiImplicitParam(value = "验证码", name = "code", dataType = "string"),
    })
    public R<MemberLoginInfo> apiLogin(String mobile, String code) {
        if (StringUtils.isEmpty(mobile)) {
            return R.error("请输入手机号");
        }
        FaMember user = memberService.lambdaQuery().eq(FaMember::getMobile, mobile).one();
        if (user == null) {
            return R.error("尚未注册");
        } else {
            if (StringUtils.isNotBlank(code)) {
                final String errorMsg = smsRecordService.checkVerifyCode(mobile, code);
                if (StringUtils.isNotBlank(errorMsg)) {
                    return R.error(errorMsg);
                }
            }else {
                return R.error("请填写验证码");
            }
        }
        final String token = convertToken(user.getId() + "");
        final MemberLoginInfo result = new MemberLoginInfo();
        BeanUtils.copyProperties(user, result);
        result.setToken(token);
        return R.success(result);
    }

    @Login
    @PostMapping("/cancelMember")
    @ApiOperation("注销账号")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "注销原因", name = "cancelReason", dataType = "string", required = true),
    })
    public R<Object> cancelMember(
            @ApiIgnore @LoginUser FaMember user,
            String cancelReason
    ) {
        String key = RedisPrefix.LOGIN_USER_TOKEN + user.getId();
        redisService.remove(key);
        memberService.lambdaUpdate().eq(FaMember::getId, user.getId())
                .set(FaMember::getName, "已注销")
                .set(FaMember::getWxname, "已注销")
                .set(FaMember::getMobile, "-")
                .set(FaMember::getCancelReason, cancelReason)
                .update();
        return R.success();
    }

    private String convertToken(String memberId) {
        LoginUserInfo userInfo = new LoginUserInfo();
        userInfo.setUserId(memberId);
        String token = "";
        try {
            token = JWTHelper.createToken(userInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
        redisService.setToken(memberId, token);
        redisService.updateUser(JSON.toJSONString(memberService.getById(memberId)), memberId);
        return token;
    }
}
